#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

namespace ns_blockqueue
{
    const int default_cap=5;

    template <class T>
    class BlockQueue
    {
    private:
        std::queue<T> bq_;//阻塞队列
        int cap_;
        pthread_mutex_t mtx_;//保护临界资源的锁
        //当生产满了就不应该生产了，应该让消费者来消费了
        //当消费空了，应该让生产者生产
        pthread_cond_t is_full_;//消费者在此条件变量下等待
        pthread_cond_t is_empty_;//生产者在该条件变量下等待
    private:
        bool IsFull()
        {
            return bq_.size()==cap_;
        }

        bool IsEmpty()
        {
            return bq_.size()==0;
        }

        void LockQueue()
        {
            pthread_mutex_lock(&mtx_);
        }

        void UnlockQueue()
        {
            pthread_mutex_unlock(&mtx_);
        }

        void ProducterWait()
        {
            //调用时会先释放自己，然后再挂起
            //返回的时候会首先自动竞争锁，获取到锁之后才能返回
            pthread_cond_wait(&is_empty_,&mtx_);
        }

        void ConsumerWait()
        {
            pthread_cond_wait(&is_full_,&mtx_);
        }

        void WakeupConsumer()
        {
            pthread_cond_signal(&is_full_);
        }

        void WakeupProducter()
        {
            pthread_cond_signal(&is_empty_);
        }
    public:
    BlockQueue(int cap = default_cap):cap_(cap)
    {
        pthread_mutex_init(&mtx_,nullptr);
        pthread_cond_init(&is_empty_,nullptr);
        pthread_cond_init(&is_full_,nullptr);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mtx_);
        pthread_cond_destroy(&is_empty_);
        pthread_cond_destroy(&is_full_);
    }
    public:
        void Push(const T &in)
        {
            LockQueue();
            while(IsFull())
            {
                ProducterWait();
            }
            //向队列放数据 
            bq_.push(in);
            WakeupConsumer();
            UnlockQueue();
        }

        void Pop(T *out)
        {
            LockQueue();
            while(IsEmpty())
            {
                ConsumerWait();
            }
            //从队列拿数据
            *out=bq_.front();
            bq_.pop();

            WakeupProducter();
            UnlockQueue();
        }
    };
}